package com.ruyuan.gateway.sdk.config;

import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.ruyuan.gateway.sdk.model.GatewayException;
import com.ruyuan.gateway.sdk.model.GatewaySDKProperties;
import com.ruyuan.gateway.sdk.realm.UserLoginRealm;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.IRedisManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;

import java.io.IOException;
import java.io.StringReader;
import java.util.Optional;
import java.util.Properties;

/**
 * @author xx
 */
@Configuration
@ComponentScan("com.ruyuan.gateway.sdk")
public class RuyuanGatewaySDKAutoConfiguration {

    private static final String DATA_ID = "base.info";

    private static final String GROUP_ID = "ruyuan.gateway";

    private static final String GATEWAY_DOMAIN = "localhost";

    @Bean
    public ConfigService gatewayConfigService() throws NacosException, IOException {
        Properties properties = new Properties();
        // 正常情况是有一个固定的网关服务域名，能够通过域名解析定位到网关服务端ip的
        CloseableHttpClient client = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("http://" + GATEWAY_DOMAIN + "/api/gateway/nacos/serverAddr");
        CloseableHttpResponse response = client.execute(httpGet);
        // 网关服务端调用失败处理
        if (response.getStatusLine().getStatusCode() != 200) {
            throw new GatewayException("call gateway server error");
        }
        String serverAddr = EntityUtils.toString(response.getEntity());
        properties.put("serverAddr", serverAddr);
        ConfigService configService = NacosFactory.createConfigService(properties);
        return configService;
    }

    @Bean
    public GatewaySDKProperties gatewaySDKProperties(ConfigService configService) throws IOException, NacosException {
        String config = configService.getConfig(DATA_ID, GROUP_ID, 3000L);
        Properties properties = new Properties();
        StringReader stringReader = new StringReader(config);
        properties.load(stringReader);
        stringReader.close();
        GatewaySDKProperties gatewaySDKProperties = new GatewaySDKProperties();
        gatewaySDKProperties.setSessionTimeout(Optional.ofNullable(properties.getProperty("session.timeout"))
                .map(Long::parseLong).orElse(1200000L));
        gatewaySDKProperties.setRedisHost(Optional.ofNullable(properties.getProperty("redis.host"))
                .orElse("localhost"));
        gatewaySDKProperties.setRedisPassword(Optional.ofNullable(properties.getProperty("redis.password"))
                .orElse(""));
        gatewaySDKProperties.setPort(Integer.parseInt(Optional.ofNullable(properties.getProperty("redis.port")).orElse("6379")));
        gatewaySDKProperties.setMaxTotal(Integer.parseInt(Optional.ofNullable(properties.getProperty("redis.max.total")).orElse("300")));
        gatewaySDKProperties.setMaxIdle(Integer.parseInt(Optional.ofNullable(properties.getProperty("redis.max.idle")).orElse("200")));
        gatewaySDKProperties.setMaxWait(Long.parseLong(Optional.ofNullable(properties.getProperty("redis.max.wait")).orElse("300")));
        return gatewaySDKProperties;
    }

    @Bean
    public SessionsSecurityManager securityManager(UserLoginRealm userLoginRealm, SessionManager redisSessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userLoginRealm);
        securityManager.setSessionManager(redisSessionManager);
        return securityManager;
    }

    @Bean
    public SessionManager redisSessionManager(SessionDAO redisSessionDAO, GatewaySDKProperties gatewaySDKProperties) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO);
        sessionManager.setGlobalSessionTimeout(gatewaySDKProperties.getSessionTimeout());
        return sessionManager;
    }

    @Bean
    public RedisSessionDAO redisSessionDAO(IRedisManager redisManager) {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager);
        return redisSessionDAO;
    }

    @Bean
    public IRedisManager redisManager(GatewaySDKProperties gatewaySDKProperties) {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(gatewaySDKProperties.getRedisHost() + ":" + gatewaySDKProperties.getPort());
        redisManager.setPassword(gatewaySDKProperties.getRedisPassword());
        return redisManager;
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition shiroFilterChainDefinition = new DefaultShiroFilterChainDefinition();
        return shiroFilterChainDefinition;
    }

    @Bean
    public RedisTemplate gatewayRedisTemplate(GatewaySDKProperties properties) {
        RedisTemplate redisTemplate = new RedisTemplate();
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(properties.getMaxIdle());
        jedisPoolConfig.setMaxTotal(properties.getMaxTotal());
        jedisPoolConfig.setMaxWaitMillis(properties.getMaxWait());
        jedisPoolConfig.setTestOnBorrow(false);
        jedisPoolConfig.setTestOnReturn(false);
        JedisConnectionFactory connectionFactory = new JedisConnectionFactory(jedisPoolConfig);
        JedisShardInfo shardInfo = new JedisShardInfo(properties.getRedisHost(), properties.getPort());
        if (StringUtils.isNotBlank(properties.getRedisPassword())) {
            shardInfo.setPassword(properties.getRedisPassword());
        }
        connectionFactory.setShardInfo(shardInfo);
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }

}
